home *** CD-ROM | disk | FTP | other *** search
- #include <stdio.h>
- #include <fcntl.h>
- #include <pwd.h>
- #include <time.h>
- #include <signal.h>
- #include <sys/types.h>
- #include <sys/stat.h>
-
- #define OPENDELAY 7 /* Days to wait before opening an account */
- #define INITBALANCE 5 /* How much to give new account openers */
-
- /* Files where bank records are kept */
- char *transfile = "/home/games/bank/trans"; /* Transaction records */
- char *bankfile = "/home/games/bank/bank"; /* Current balances */
- char *bankers = "/home/games/bank/bankers"; /* List of Bankers */
-
- #ifdef FILELOCK
- char *translock = "/home/games/bank/t.lock";
- char *banklock = "/home/games/bank/b.lock";
- #else
- #define done(r) exit(r)
- #endif FILELOCK
-
- /* Names of monetary units and of the bank */
- char *bankname = "the First National Bank of The R.I.P.";
- char *unit = "fuzzy-nibble";
- char *units = "fibblies";
- char *abbr = "fb";
-
- /* Structure of transaction file entry */
- struct trans_ent {
- int to_uid; /* Uid of person affected */
- char code; /* See below */
- char fr_id[8]; /* Login id of donor */
- char why[40]; /* Donor's reason why */
- long amount; /* Number of units */
- };
-
- /* Transaction codes */
- #define TR_NONE 0 /* Not a real entry */
- #define TR_XFER 1 /* From another user */
- #define TR_GIFT 2 /* Bestowal from the government */
- #define TR_FINE 3 /* Fine from the government */
-
- char *trname[4] = {"bug","transfer","gift","fine"};
-
- /* Global variables */
- int myuid; /* Customer's uid number */
- char myid[9]; /* Customer's login name */
- int banker; /* Is the customer a banker? */
- int report; /* Should I just report new transactions? */
- long mybal; /* Customer's current balance */
- char buf[42]; /* Input buffer */
- char hisid[42]; /* His login name */
- long tm; /* Current time */
- int bf; /* Bank file stream */
- int tf; /* Transaction file stream */
-
- struct passwd *getpwuid(), *getpwnam();
- struct tm *localtime();
- long time(),atol(),lseek();
- char *ctime();
-
- main(argc,argv)
- int argc;
- char **argv;
- {
- struct passwd *pwd;
- int i;
-
- if ((bf = open(bankfile,O_RDWR|O_CREAT,0700)) < 0)
- {
- printf("Error: Cannot open bank file %s\n",bankfile);
- done(1);
- }
- if ((tf = open(transfile,O_RDWR|O_CREAT,0600)) < 0)
- {
- printf("Cannot open transaction file %s\n",transfile);
- done(1);
- }
-
- tm = time((long *)0);
-
- pwd = getpwuid(myuid = getuid());
- strncpy(myid,pwd->pw_name,8);
- banker = am_banker(myid);
-
- if (argc > 1)
- {
- if (argv[1][0] != '-')
- {
- for (i=1; i<argc; i++)
- {
- setpwent();
- printf("%8s ",argv[i]);
- if ((pwd = getpwnam(argv[i])) == NULL)
- printf("not found\n");
- else if (balance(pwd->pw_uid,&mybal))
- printf("has no account\n");
- else
- printf("balance = %ld%s\n",mybal,abbr);
- }
- done(0);
- }
-
- switch(argv[1][1])
- {
- case 'w':
- wealthy();
- exit(0);
- case 'a':
- listall();
- exit(0);
- case 'r':
- report = 1;
- banker = 0;
- break;
- case 't':
- parsetrans(TR_XFER,argc,argv);
- done(0);
- case 'b':
- parsetrans(TR_GIFT,argc,argv);
- done(0);
- case 'f':
- parsetrans(TR_FINE,argc,argv);
- done(0);
- case 'o':
- case 'c':
- if (!banker)
- {
- printf("You're not a banker!\n");
- done(1);
- }
- if (argc != 3)
- {
- printf("usage: %s -%c userid\n",argv[0],argv[1][1]);
- done(1);
- }
- setpwent();
- if ((pwd = getpwnam(argv[2])) == NULL)
- {
- printf("Can't find user %s\n",argv[2]);
- done(1);
- }
- if (argv[1][1] == 'o')
- addbalance(pwd->pw_uid,0L);
- else
- closeaccount(pwd->pw_uid);
- done(0);
- default:
- printf("Legal options are -w -a -t ");
- if (banker) printf("-b -f -o -c ");
- printf("and -r\n");
- done(1);
- }
- }
-
- if (banker)
- {
- struct tm *tod;
- printf("Good ");
- tod = localtime(&tm);
- if (tod->tm_hour < 12)
- printf("Morning");
- else if (tod->tm_hour < 18)
- printf("Afternoon");
- else
- printf("Evening");
- printf(", Banker %s\n",myid);
- }
-
- if (!report)
- printf("Welcome to %s\n\n",bankname);
-
- checkme(); /* Only returns if user has open account */
-
- printf("Transactions since your last visit: ");
- scantrans(myuid,0);
-
- printf("\nCurrent balance: %ld%s\n",mybal,abbr);
-
- if (!report)
- {
- if (banker)
- dobank();
- else if (mybal > 0)
- {
- printf("\nWould you like to transfer funds to another user? ");
- fgets(buf,40,stdin);
- if (buf[0] != 'n' && buf[0] != 'N')
- asktrans(TR_XFER,NULL);
- }
- }
- done(0);
- }
-
- /*
- * Register the user
- */
-
- checkme()
- {
- int ch;
-
- switch (balance(myuid,&mybal))
- {
- case 1:
- if (report)
- {
- printf("You have no account\n");
- done(0);
- }
- if (banker)
- {
- addbalance(myuid,0L);
- mybal = INITBALANCE;
- return;
- }
- printf("Hello %s, would you like to open an account? ",myid);
- if ((ch = getchar()) == 'n' || ch == 'N')
- {
- printf("Goodbye then...\n");
- done(0);
- }
- if (ch != '\n')
- while (getchar() != '\n')
- ;
- openaccount(myuid);
- printf("Very well...\nThere will be a %d day delay ",OPENDELAY);
- printf("before your account can be opened.\n Thank you!\n");
- done(0);
- case 2: /* Applicant awaiting approval */
- if (mybal > tm)
- {
- if (report)
- {
- printf("Your account will be open after %s",ctime(&mybal));
- done(0);
- }
- else if (!banker)
- {
- printf("Sorry, the paperwork on your new account has not ");
- printf("been completed\nPlease come back after %s",
- ctime(&mybal));
- printf(" Thank you!\n");
- done(0);
- }
- }
- /* If time is up, initialize the account */
- addbalance(myuid,0L);
- mybal = INITBALANCE;
- break;
- }
- }
-
-
- parsetrans(type,argc,argv)
- int type, argc;
- char **argv;
- {
- long amt;
- struct passwd *pwd;
-
- if (type != TR_XFER && !banker)
- {
- printf("You're not a banker!\n");
- return;
- }
-
- if (argc < 4 || argc > 5)
- {
- printf("usage: %s [-t",argv[0]);
- if (banker) printf(" -b -f");
- printf("] who amount [reason]\n");
- return;
- }
-
- setpwent();
- if ((pwd = getpwnam(argv[2])) == NULL)
- {
- printf("No user %s\n",argv[2]);
- return;
- }
-
- if ((amt = atol(argv[3])) <= 0)
- {
- printf("Bad amount %s\n",argv[3]);
- return;
- }
-
- checkme();
-
- if (type == TR_XFER && amt > mybal)
- {
- printf("You don't have that much money\n");
- return;
- }
-
- if (myuid == pwd->pw_uid)
- {
- printf("Can't transfer money to yourself\n");
- return;
- }
-
- if (argc == 4)
- {
- buf[0] = '\0';
- dotrans(type,pwd->pw_uid,amt,buf);
- }
- else
- dotrans(type,pwd->pw_uid,amt,argv[4]);
- }
-
-
- /*
- * The banker's secretary.
- */
-
- dobank()
- {
- char *w;
- printf("\nYou may either:\n");
- printf(" Transfer some of your own money to another user\n");
- printf(" Fine another user some money\n");
- printf(" Bestow some money on another user\n");
- printf(" Open an account for another user\n");
- printf(" Close a users account\n");
- printf(" Report on an account's balance\n");
- printf(" Quit\n");
-
- for (;;)
- {
- printf(": ");
- buf[0] = '\0';
- fgets(buf,40,stdin);
- for (w = buf; *w != '\0'; w++)
- if (*w == ' ')
- {
- w++;
- w[strlen(w)-1] = '\0';
- break;
- }
- switch(buf[0])
- {
- case 'T': case 't':
- asktrans(TR_XFER,w);
- break;
- case 'F': case 'f':
- asktrans(TR_FINE,w);
- break;
- case 'B': case 'b':
- asktrans(TR_GIFT,w);
- break;
- case 'O': case 'o':
- askopen(w);
- break;
- case 'C': case 'c':
- askclose(w);
- break;
- case 'R': case 'r':
- askreport(w);
- case '\n':
- break;
- case '\0':
- putchar('\n');
- case 'Q': case 'q':
- return;
- default:
- printf("Pardon me?\n");
- break;
- }
- }
- }
-
- /*
- * Ask user about a transaction of type type.
- */
-
- asktrans(type,did)
- int type;
- char *did;
- {
- int hisuid;
- long hisbal,amt;
- int n;
-
- /* Find out who to pay */
- if (did == NULL || *did == '\0')
- {
- switch (type)
- {
- case TR_XFER:
- printf("Transfer to");
- break;
- case TR_GIFT:
- printf("Bestow upon");
- break;
- case TR_FINE:
- printf("Fine");
- break;
- }
-
- printf(" whom? ");
- }
- if ((hisuid = getwho(did)) < 0)
- return;
-
- if (hisuid == myuid)
- {
- printf("Why, that's you!\n");
- return;
- }
-
- /* Get his account status and balance */
- switch(balance(hisuid,&hisbal))
- {
- case 1:
- if (!banker)
- {
- printf("%s has not applied for an account\n",hisid);
- printf("However, we will open one in their name in %d days\n",
- OPENDELAY);
- openaccount(hisuid);
- return;
- }
- addbalance(hisuid,0L);
- break;
- case 2:
- if (!banker && hisbal < tm)
- {
- printf("%s's account will be open on %s",hisid,ctime(&hisbal));
- return;
- }
- addbalance(hisuid,0L);
- break;
- }
-
- /* Find out how much to pay him */
- printf("How many %s? ",units);
- fgets(buf,40,stdin);
- amt = atol(buf);
-
- if (amt <= 0) return;
-
- if (type == TR_XFER && amt > mybal)
- {
- printf("You only have %ld %s\n",mybal,(mybal == 1)?unit:units);
- return;
- }
-
- /* Get a short explaination message */
- for (;;)
- {
- printf("What for? ");
- fgets(buf,40,stdin);
- n = strlen(buf);
- if (buf[n-1] == '\n') break;
- printf("Only 40 characters, please.\n");
- while (getchar() != '\n')
- ;
- }
- buf[n-1] = '\0';
-
- dotrans(type,hisuid,amt,buf);
- }
-
-
- /*
- * Record a transaction of type type to user uid of amount amt for reason
- * reason.
- */
-
- dotrans(type,uid,amt,reason)
- int type,uid;
- long amt;
- char *reason;
- {
- struct trans_ent tr;
-
- /* Assemble the transaction record */
- tr.code = type;
- tr.to_uid = uid;
- tr.amount = amt;
- strncpy(tr.fr_id,myid,8);
- strncpy(tr.why,reason,40);
-
- signal(SIGINT,SIG_IGN);
- signal(SIGQUIT,SIG_IGN);
-
- if (type == TR_XFER) addbalance(myuid,-amt);
- addbalance(uid,(type == TR_FINE)?-amt:amt);
-
- transaction(tr);
-
- signal(SIGINT,SIG_DFL);
- signal(SIGQUIT,SIG_DFL);
- }
-
-
- /*
- * Instantly open an account for someone, speeding up the paper work.
- */
-
- askopen(did)
- char *did;
- {
- int hisuid;
-
- /* Find out whose account to open */
- if (did == NULL || *did == '\0')
- printf("Open for whom? ");
- if ((hisuid = getwho(did)) < 0)
- return;
-
- addbalance(hisuid,0L);
- }
-
- askclose(did)
- char *did;
- {
- int hisuid;
-
- /* Find out whose account to open */
- if (did == NULL || *did == '\0')
- printf("Close whom? ");
- if ((hisuid = getwho(did)) < 0)
- return;
-
- closeaccount(hisuid);
- }
-
-
- /*
- * Report on another user
- */
-
- askreport(did)
- char *did;
- {
- int hisuid;
- long amt;
-
- /* Find out whose account to report */
- if (did == NULL || *did == '\0')
- printf("Report on whom? ");
- if ((hisuid = getwho(did)) < 0)
- return;
-
- switch (balance(hisuid,&amt))
- {
- case 1:
- printf("no account\n");
- break;
- case 2:
- printf("account opens on %s",ctime(&amt));
- break;
- case 0:
- printf("balance =%3ld%s\n",amt,abbr);
- break;
- }
- }
-
- /*
- * Find a free spot in the transaction file and put a new transaction in it.
- * take care to handle files that have been truncated to odd lengths by disk
- * full errors.
- */
-
- transaction(tr)
- struct trans_ent tr;
- {
- int n;
- struct trans_ent otr;
-
- lseek(tf,0L,0);
- #ifdef LOCKFILE
- setlock(1);
- #else
- #endif LOCKFILE
-
- /* Skip to first null entry, or EOF */
- while((n = read(tf,&otr,sizeof(otr))) == sizeof(otr) &&
- otr.code != TR_NONE)
- ;
-
- /* If not at EOF, rewind to beginning of last read */
- if (n != 0) lseek(tf,(long) -n, 1);
-
- write(tf,&tr,sizeof(tr));
-
- #ifdef LOCKFILE
- unlock(1);
- #else
- lseek(tf,0L,0);
- #endif LOCKFILE
- }
-
- /*
- * Read in someone's name, and return his uid. Return -1 if a bad name is
- * given.
- */
-
- getwho(did)
- char *did;
- {
- struct passwd *pwd;
-
- if (did == NULL || *did == '\0')
- {
- fgets(hisid,40,stdin);
- hisid[strlen(hisid)-1] = '\0';
- }
- else
- strcpy(hisid,did);
-
- setpwent();
- if ((pwd = getpwnam(hisid)) == NULL)
- {
- printf("No such user (%s)\n",hisid);
- return(-1);
- }
- return(pwd->pw_uid);
- }
-
- /*
- * Report all tranactions involving uid which have not been previously
- * reported. Transaction records are set to null after they have been
- * printed. If silent is set, nothing is printed.
- */
-
- scantrans(uid,silent)
- int uid;
- int silent;
- {
- struct trans_ent tr;
- long offset;
- int someflag = 0;
-
- lseek(tf,offset = 0L,0);
- while (read(tf,&tr,sizeof(tr)) == sizeof(tr))
- {
- if (tr.code != TR_NONE && tr.to_uid == uid)
- {
- if (!silent)
- {
- someflag = 1;
- printf("\n%7ld%s %s from %0.8s",tr.amount,abbr,
- trname[tr.code],tr.fr_id);
- if (tr.why[0] != '\0')
- printf(" for %0.40s",tr.why);
- }
-
- tr.code = TR_NONE;
- lseek(tf,offset,0);
- write(tf,&tr,sizeof(tr));
- }
- offset += sizeof(tr);
- }
-
- if (!someflag && !silent)
- printf("None");
- }
-
-
- /*
- * Place a user's bank balance in *bal. Function returns 0 if successful,
- * 1 if that user hasn't requested an account, and 2 if the account has been
- * requested, but not opened. In the latter case, *bal contains the date on
- * which it will be opened.
- */
-
- balance(uid,bal)
- int uid;
- long *bal;
- {
-
- if (lseek(bf,((long)uid)*sizeof(long),0) < 0)
- return(1);
-
- if (read(bf,bal,sizeof(long)) != sizeof(long))
- return(1);
-
- if ((*bal % 2) == 0)
- return((*bal == 0)?1:2);
-
- *bal /= 2;
- return(0);
- }
-
- /*
- * Write a new value into a user's account.
- */
-
- writebalance(uid,bal)
- int uid;
- long bal;
- {
- if (lseek(bf,((long)uid)*sizeof(long),0) >= 0)
- write(bf,&bal,sizeof(long));
- }
-
- /*
- * Increase the balance of uid's account by bal. Open it and put in the
- * initial deposit if it was not open.
- */
-
- addbalance(uid,bal)
- int uid;
- long bal;
- {
- long oldbal;
- if (lseek(bf,((long)uid)*sizeof(long),0) < 0)
- return;
-
- #ifdef LOCKFILE
- setlock(0);
- #else
- #endif LOCKFILE
-
- if (read(bf,&oldbal,sizeof(long)) != sizeof(long) || (oldbal % 2) == 0)
- oldbal = (long)INITBALANCE;
- else
- oldbal /= 2;
-
- writebalance(uid,(bal+oldbal)*2+1);
-
- #ifdef LOCKFILE
- unlock(0);
- #else
- lseek(bf,-(long)sizeof(long),1);
- #endif LOCKFILE
- }
-
- /*
- * Create an account with the usual time lag.
- */
-
- openaccount(uid)
- int uid;
- {
- long tm,time();
- #define SECSPERDAY 86400L;
-
- tm = time((long *)0) + OPENDELAY*SECSPERDAY;
- if ((tm % 2) == 1) tm--;
- writebalance(uid,tm);
- }
-
- closeaccount(uid)
- int uid;
- {
- writebalance(uid,0L);
- scantrans(uid,1);
- }
-
- /*
- * List the 10 wealthiest users.
- */
-
- wealthy()
- {
- struct passwd *pwd;
- struct {
- int uid;
- long bucks;
- } rich[10];
- int n=0,i;
- long new;
- int cuid;
-
- lseek(bf,0L,0);
-
- if (read(bf,&new,sizeof(new)) != sizeof(new))
- return;
-
- rich[0].uid = 0;
- rich[0].bucks = new;
-
- for (cuid = 1; read(bf,&new,sizeof(new)) == sizeof(new); cuid++)
- {
- if ((new % 2) == 0) continue;
- new = (new-1)/2;
-
- for (i = n; i > 0 && new > rich[i-1].bucks; i--)
- {
- if (i < 10)
- {
- rich[i].uid = rich[i-1].uid;
- rich[i].bucks = rich[i-1].bucks;
- }
- }
- if (i < 10)
- {
- rich[i].uid = cuid;
- rich[i].bucks = new;
- if (n < 10) n++;
- }
- }
-
- for (i = 0; i < n; i++)
- {
- setpwent();
- if ((pwd = getpwuid(rich[i].uid)) == NULL)
- closeaccount(rich[i].uid);
- else
- printf("%2d: %8.8s%12ld%s\n",i+1,pwd->pw_name,rich[i].bucks,abbr);
- }
- }
-
- listall()
- {
- struct passwd *pwd;
- long bal;
- int cuid;
-
- lseek(bf,0L,0);
-
- for (cuid = 0; read(bf,&bal,sizeof(bal)) == sizeof(bal); cuid++)
- {
- if ((bal % 2) == 0) continue;
- bal = (bal-1)/2;
- setpwent();
- if ((pwd = getpwuid(cuid)) == NULL)
- closeaccount(cuid);
- else
- printf("%8.8s%12ld%s\n",pwd->pw_name,bal,abbr);
- }
- }
-
- #ifdef LOCKFILE
- /*
- * Create a lockfile. If which is 0, this is the bank lock file. If which
- * is 1, it is the transaction lockfile.
- */
-
- long clock[2] = { 0L , 0L };
-
- setlock(which)
- int which;
- {
- int lf;
- register int trys = 0;
- struct stat sb;
- char *lockfile = which?translock:banklock;
-
- /* Already locked? */
- if (clock[which])
- return;
-
- /* Do exclusive open on file */
- while ((lf = open(lockfile,O_RDONLY|O_CREAT|O_EXCL,0700)) < 0)
- {
- /* Examine existing lock file */
- if (stat(lockfile,&sb))
- {
- printf("Can't create lockfile %s\n",lockfile);
- done(1);
- }
- /* If more than 5 minutes old, kill it */
- if (sb.st_ctime + 300 < time((long *)0))
- {
- printf("Blasting old %s lockfile\n",
- which?"transaction":"bank");
- if (unlink(lockfile))
- {
- printf("blast failed\n");
- done(1);
- }
- continue;
- }
- /* Wait up to 15 seconds for old lockfile go away */
- else if (trys++ < 5)
- sleep(3);
- else
- {
- /* Give up waiting */
- printf("The bank is very busy right now.\n");
- printf("Please try later.\n");
- done(1);
- }
- }
-
- /* Record the time at which I create the file */
- fstat(lf,&sb);
- clock[which] = sb.st_ctime;
- close(lf);
- }
-
- unlock(which)
- int which;
- {
- char *lockfile = which?translock:banklock;
- struct stat sb;
-
- if (!clock[which]) return;
-
- /* Check if it is my lock file */
- if (stat(lockfile,&sb) || sb.st_ctime != clock[which])
- printf("Warning: %s lock file has been blasted.\n",
- which?"transaction":"bank");
- /* If so, blast it */
- else if (unlink(lockfile))
- printf("Warning: Can't remove lockfile %s\n",lockfile);
-
- clock[which] = 0L;
- }
-
- done(code)
- int code;
- {
- if (clock[0]) unlock(0);
- if (clock[1]) unlock(1);
-
- exit(code);
- }
- #else
- #endif LOCKFILE
-
- /* Determine if the user "id" is a banker */
-
- am_banker(id)
- char *id;
- {
- FILE *fp;
- int idlen = strlen(id);
- char buf[21];
-
- if ((fp=fopen(bankers,"r")) == NULL)
- {
- printf("cannot read %s\n",bankers);
- return(0);
- }
-
- while(fgets(buf,20,fp) != NULL)
- if (!strncmp(id,buf,idlen))
- return(1);
- return(0);
- }
-